﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Reflection;
using Ping.Core;
using log4net;

namespace Ping.Server.Jobs
{
	public sealed class TracerouteJob
		: Job
	{
		private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
		private static readonly byte[] Buffer = new byte[32];

		public override Type RequestType
		{
			get { return typeof (TracerouteRequest); }
		}

		public override Type ResultType
		{
			get { return typeof (TracerouteResult); }
		}

		public override string Name
		{
			get { return "Traceroute"; }
		}

		public override Result Perform(Request request)
		{
			var tmp = (TracerouteRequest) request;
			var hops = new List<PingResult>();
			var watch = new Stopwatch();

			if (Log.IsDebugEnabled)
				Log.DebugFormat("Performing Trace to {0}, max. Hops: {1}", tmp.Destination, tmp.MaxHops);

			using (var sender = new System.Net.NetworkInformation.Ping())
			{
				var options = new PingOptions
				              	{
				              		DontFragment = true,
				              		Ttl = 1,
				              	};

				for (int i = 0; i < tmp.MaxHops; ++i)
				{
					watch.Restart();
					PingReply reply = sender.Send(
						tmp.Destination.Native,
						(int) tmp.Timeout.TotalMilliseconds,
						Buffer,
						options);
					watch.Stop();

					if (reply == null)
						throw new Exception("The Ping API didn't return a result");

					hops.Add(new PingResult
					            	{
					            		Sender = new IpAddress(reply.Address),
					            		Status = (IpStatus) reply.Status,
					            		Rtt = reply.RoundtripTime == 0 ? watch.Elapsed : TimeSpan.FromMilliseconds(reply.RoundtripTime),
					            	});

					if (reply.Status == IPStatus.Success)
						break;

					// Go to the next hop...
					++options.Ttl;
				}
			}

			return new TracerouteResult {Hops = hops.ToArray()};
		}
	}
}